/*****************************************
 * Copyright (C) 2022 * Ltd. All rights reserved.
 *
 * File name   : application.cpp
 * Author      : longbin
 * Created date: 2022-03-11 14:25:26
 * Description :
 *
 *******************************************/

#include <iostream>
#include <iterator>
#include <string>
#include <vector>
#include <map>
#include <numeric>

#include <unistd.h>
#include <signal.h>
#include <cassert>

#include "HandlerLooper.hpp"

#ifdef LOG_TAG
#    undef LOG_TAG
#endif // LOG_TAG
#define LOG_TAG "HLApplication"

#include "Range.hpp"
#include "logging.hpp"
// #include "libcallstack.h"

template <class S>
std::string join(const S &sequence, const std::string &delim = "")
{
    if (0 == sequence.size()) {
        return {};
    }
    using U = typename std::decay<typename S::value_type>::type;
    std::ostringstream oss{};
    std::copy(sequence.begin(), sequence.end(), std::ostream_iterator<U>(oss, delim.c_str()));
    std::string res(oss.str());
    return {res.c_str(), res.length() - delim.length()};
}

typedef struct ST {
    int id;
    std::string info;
} ST;

class MyHandler : public handlerlooper::HLHandler
{
public:
    MyHandler()
    {}

    explicit MyHandler(const handlerlooper::HLLooperRefPtr &looper)
        : handlerlooper::HLHandler(looper)
    {}

    ~MyHandler()
    {
        LOGI("~MyHandler()");
    }

    void handleMessage(const handlerlooper::HLMessageRefPtr &msg) override;
};

// override handleMessage to process msg.
void MyHandler::handleMessage(const handlerlooper::HLMessageRefPtr &msg)
{
    LOGI("MyHandler::handleMessage what:%d, when:%lld", msg->what,
         msg->when);

    switch (msg->what) {
        case 0: {
            LOGI("what:%d", msg->what);
            break;
        }
        case 1: {
            LOGI("what:%d, arg1:%d", msg->what, msg->arg1);
            break;
        }
        case 2: {
            LOGI("what:%d, arg1:%d, arg2:%d", msg->what, msg->arg1, msg->arg2);
            break;
        }
        case 3: {
            LOGI("what:%d, arg1:%d, arg2:%d", msg->what, msg->arg1, msg->arg2);
            int i = handlerlooper::cast_any_obj<int>(msg->obj);
            LOGI("obj is int:%d", i);
            break;
        }
        case 4: {
            LOGI("what:%d, arg1:%d, arg2:%d", msg->what, msg->arg1, msg->arg2);
            std::string s = handlerlooper::cast_any_obj<std::string>(msg->obj);
            LOGI("obj is string:%s", s.c_str());
            break;
        }
        case 5: {
            LOGI("what:%d, arg1:%d, arg2:%d", msg->what, msg->arg1, msg->arg2);
            std::vector<int> v = handlerlooper::cast_any_obj<std::vector<int>>(msg->obj);
            LOGI("obj is vector int, has:%s", join(v, ", ").c_str());
            break;
        }
        case 6: {
            LOGI("what:%d, arg1:%d, arg2:%d", msg->what, msg->arg1, msg->arg2);
            ST st = handlerlooper::cast_any_obj<ST>(msg->obj);
            LOGI("obj is ST, has id:%d, info:%s", st.id, st.info.c_str());
            break;
        }
        default: {
            break;
        }
    }
}

void handleMessage(const handlerlooper::HLMessageRefPtr &msg)
{
    LOGI("handleMessage what:%d, when:%lld", msg->what, msg->when);
}

void signalHandler(int signo)
{
    // LOGI("signalHandler received signal: %d", signo);
    // callstack_dump("CALLSTACK_DUMP: ");

    // reset signal handle to default
    signal(signo, SIG_DFL);
    // will receive SIGSEGV again and exit app
}

int main()
{
    signal(SIGSEGV, signalHandler);

    auto handler1 = std::thread([]() {
        // 1. create an instance of HLHandlerThread. then start() the handler thread.
        auto ht1 = std::make_shared<handlerlooper::HLHandlerThread>("ht1");
        LOGI("ht1 start.");
        ht1->start();

        assert(ht1->getLooper());
        LOGI("ht1 name:%s", ht1->name().c_str());

        // 2. create an instance derived from HLHandler with the handler thread looper.
        //    or create a HLHandler instance with the handler thread looper and self-defined
        //    function.
        auto handler = std::make_shared<MyHandler>(ht1->getLooper());

        // 3. use the handler to send message or post method.
        // test post callback
        handler->post([](const handlerlooper::HLMessageRefPtr &) {
            LOGI("post callback test.");
            return true;
        });

        handler->postDelayed(
            [](const handlerlooper::HLMessageRefPtr &) {
                LOGI("postDelayed callback test.");
                return true;
            },
            1000);

        // test what arg1 arg2 arg3
        handler->sendMessage(0);
        handler->sendMessage(1, 2);
        handler->sendMessage(2, 4, 5);
        std::this_thread::sleep_for(std::chrono::milliseconds(10));

        // test obj int
        handler->sendMessage(3, 4, 5, 0);
        handler->sendMessage(3, 4, 5, handlerlooper::make_any_obj<int>(8));
        handler->sendMessage(3, 4, 5, handlerlooper::make_any_obj<int>(9));

        // test obj string
        std::string s4 = "hello world";
        handler->sendMessage(4, 5, 6, handlerlooper::make_any_obj<std::string>(s4));

        handler->sendMessage(4, 5, 6, handlerlooper::make_any_obj<std::string>("hello string"));

        // test obj vector
        std::vector<int> v5{1, 2, 3, 4, 5};
        handler->sendMessage(5, 6, 7, handlerlooper::make_any_obj<std::vector<int>>(v5));

        std::vector<int> v52(10);
        std::iota(v52.begin(), v52.end(), 100);
        handler->sendMessage(5, 6, 7, handlerlooper::make_any_obj<std::vector<int>>(v52));

        handler->sendMessage(5, 6, 7, handlerlooper::make_any_obj<std::vector<int>>(1, 2, 3, 4, 5));

        // test obj struct
        ST st{1, "hello ST 1"};
        handler->sendMessage(6, 7, 8, handlerlooper::make_any_obj<ST>(st));

        handler->sendMessage(6, 7, 8, handlerlooper::make_any_obj<ST>(2, "hello ST 2"));
        std::this_thread::sleep_for(std::chrono::milliseconds(10));

        // test what arg1 arg2 arg3 when
        handler->sendMessageDelayed(7, (long long)0);
        handler->sendMessageDelayed(8, 9, (long long)0);
        handler->sendMessageDelayed(9, 9, 9, (long long)0);
        handler->sendMessageDelayed(10, (long long)1000);
        handler->sendMessageDelayed(11, 12, (long long)1000);
        handler->sendMessageDelayed(12, 13, 14, (long long)2000);

        // test remove message
        handler->sendMessageDelayed(13, 14, 15, (long long)1000);
        LOGI("hasMessages %d:%s", 13, handler->hasMessages(13) ? "yes" : "no");
        handler->removeMessages(13);
        LOGI("hasMessages %d:%s", 13, handler->hasMessages(13) ? "yes" : "no");
        handler->sendMessageDelayed(14, 15, 16, (long long)10000);

        std::this_thread::sleep_for(std::chrono::milliseconds(1500));
        handler->removeMessages(14);
        handler->removeMessages(12);
        LOGI("removeMessages 12");
        LOGI("hasMessages %d:%s", 12, handler->hasMessages(12) ? "yes" : "no");
        std::this_thread::sleep_for(std::chrono::milliseconds(3000));

        // 4. call quit() and stop() method of handler thread to terminate.
        LOGI("ht1 quit.");
        ht1->quit();
        LOGI("ht1 stop.");
        ht1->stop();
    });

    auto handler2 = std::function<void()>([]() {
        // 1. create an instance of HLHandlerThread. then start() the handler thread.
        auto ht2 = std::make_shared<handlerlooper::HLHandlerThread>("ht2");
        LOGI("ht2 start.");
        ht2->start();

        assert(ht2->getLooper());
        LOGI("ht2 name:%s", ht2->name().c_str());

        // 2. create an instance derived from HLHandler with the handler thread looper.
        //    or create a HLHandler instance with the handler thread looper and self-defined
        //    function.
        auto handler = std::make_shared<handlerlooper::HLHandler>(
            ht2->getLooper(), [](const handlerlooper::HLMessageRefPtr &msg) {
                handleMessage(msg);
                return true;
            });

        // 3. use the handler to send message or post method.
        LOGI("performance check begin");
        // for (auto seq = 20; seq < 300001; seq++) {
        for (const auto &seq : range::range(20, 300001)) {
            handler->sendMessage(seq);
            if (0 == seq % 100) {
                LOGI("hasMessages %d:%s", seq, handler->hasMessages(seq) ? "yes" : "no");
            }
        }
        LOGI("performance check end");
        std::this_thread::sleep_for(std::chrono::milliseconds(3000));

        // 4. call quit() and stop() method of handler thread to terminate.
        LOGI("ht2 quit.");
        ht2->quit();
        LOGI("ht2 stop.");
        ht2->stop();
    });

    handler2();
    if (handler1.joinable()) {
        handler1.join();
    }
    return 0;
}
